﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.D3DCompiler;
using SlimDX;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using System.Runtime.InteropServices;
using System.Drawing;
using Apparat.ShaderManagement;

namespace Apparat.Renderables
{
    public class TextureBox : Renderable
    {
        SlimDX.Direct3D11.Buffer vertexBuffer;
        SlimDX.Direct3D11.Buffer indexBuffer;
        Vector3 minXminY, minXmaxY, maxXminY, maxXmaxY;
        DataStream vertices;
        DataStream indices;
        int numVertices = 0;
        int vertexStride = 0;

        Color4 diffuse = new Color4(1, 0, 1, 0);
        Texture3D texture;


        ShaderResourceView m_textureView;


        int vertexBufferSizeInBytes;
        int indexBufferSizeInBytes;
        int numIndices = 0;
        int indexStride = 0;

        public TextureBox(Vector3 min, Vector3 max)
        {
            //float y = (min.Y + max.Y) / 2.0f;
            //minXminY = min;
            //maxXmaxY = max;
            //minXmaxY = new Vector3(min.X, y, max.Z);
            //maxXminY = new Vector3(max.X, y, min.Z);

            //numVertices = 6;
            //vertexStride = 20;
            //int SizeOfVertexBufferInBytes = numVertices * vertexStride;
            //vertices = new DataStream(SizeOfVertexBufferInBytes, true, true);

            //vertices.Write(min); vertices.Write(new Vector2(0, 1));
            //vertices.Write(minXmaxY); vertices.Write(new Vector2(0, 0));
            //vertices.Write(max); vertices.Write(new Vector2(1, 0));

            //vertices.Write(max); vertices.Write(new Vector2(1, 0));
            //vertices.Write(maxXminY); vertices.Write(new Vector2(1, 1));
            //vertices.Write(min); vertices.Write(new Vector2(0, 1));

            //vertices.Position = 0;

            //// create the vertex buffer
            //vertexBuffer = new SlimDX.Direct3D11.Buffer(DeviceManager.Instance.device,
            //    vertices,
            //    SizeOfVertexBufferInBytes,
            //    ResourceUsage.Default,
            //    BindFlags.VertexBuffer,
            //    CpuAccessFlags.None,
            //    ResourceOptionFlags.None,
            //    0);

            // half length of an edge
            float offset = 1.5f;

            vertexStride = Marshal.SizeOf(typeof(PositionTextured3DVertex)); // 16 bytes
            numVertices = 8;
            vertexBufferSizeInBytes = vertexStride * numVertices;

            vertices = new DataStream(vertexBufferSizeInBytes, true, true);

            vertices.Write(new PositionTextured3DVertex(new Vector3(+offset, +offset, +offset), new Vector3(1, 0, 1))); // 0
            vertices.Write(new PositionTextured3DVertex(new Vector3(+offset, +offset, -offset), new Vector3(1, 0, 0))); // 1
            vertices.Write(new PositionTextured3DVertex(new Vector3(-offset, +offset, -offset), new Vector3(0, 0, 0))); // 2
            vertices.Write(new PositionTextured3DVertex(new Vector3(-offset, +offset, +offset), new Vector3(0, 0, 1))); // 3

            vertices.Write(new PositionTextured3DVertex(new Vector3(-offset, -offset, +offset), new Vector3(0, 1, 1))); // 4
            vertices.Write(new PositionTextured3DVertex(new Vector3(+offset, -offset, +offset), new Vector3(1, 1, 1))); // 5
            vertices.Write(new PositionTextured3DVertex(new Vector3(+offset, -offset, -offset), new Vector3(1, 1, 0))); // 6
            vertices.Write(new PositionTextured3DVertex(new Vector3(-offset, -offset, -offset), new Vector3(0, 1, 0))); // 7


            vertices.Position = 0;

            vertexBuffer = new SlimDX.Direct3D11.Buffer(
               DeviceManager.Instance.device,
               vertices,
               vertexBufferSizeInBytes,
               ResourceUsage.Default,
               BindFlags.VertexBuffer,
               CpuAccessFlags.None,
               ResourceOptionFlags.None,
               0);

            numIndices = 36;
            indexStride = Marshal.SizeOf(typeof(short)); // 2 bytes
            indexBufferSizeInBytes = numIndices * indexStride;

            indices = new DataStream(indexBufferSizeInBytes, true, true);

            // Cube has 6 sides: top, bottom, left, right, front, back
            // top
            indices.WriteRange(new short[] { 0, 1, 2 });
            indices.WriteRange(new short[] { 2, 3, 0 });

            // right
            indices.WriteRange(new short[] { 0, 5, 6 });
            indices.WriteRange(new short[] { 6, 1, 0 });

            // left
            indices.WriteRange(new short[] { 2, 7, 4 });
            indices.WriteRange(new short[] { 4, 3, 2 });

            // front
            indices.WriteRange(new short[] { 1, 6, 7 });
            indices.WriteRange(new short[] { 7, 2, 1 });

            // back
            indices.WriteRange(new short[] { 3, 4, 5 });
            indices.WriteRange(new short[] { 5, 0, 3 });

            // bottom
            indices.WriteRange(new short[] { 6, 5, 4 });
            indices.WriteRange(new short[] { 4, 7, 6 });

            indices.Position = 0;

            indexBuffer = new SlimDX.Direct3D11.Buffer(
                DeviceManager.Instance.device,
                indices,
                indexBufferSizeInBytes,
                ResourceUsage.Default,
                BindFlags.IndexBuffer,
                CpuAccessFlags.None,
                ResourceOptionFlags.None,
                0);
        }

        public TextureBox(Vector3 min, Vector3 max, string PicturePath)
            : this(min, max)
        {
            try
            {
                LoadPicture(PicturePath);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void LoadPicture(string path)
        {
            texture = Texture3D.FromFile(DeviceManager.Instance.device, path);
            m_textureView = new ShaderResourceView(DeviceManager.Instance.device, texture);
        }

        public void SetTexture(Byte[] data, int width, int height, int depth)
        {
            try
            {

                var loadInfo = new ImageLoadInformation
                {
                    BindFlags = BindFlags.ShaderResource,
                    CpuAccessFlags = CpuAccessFlags.Write,
                    Depth = depth,
                    Width = width,
                    Height = height,
                    Format = SlimDX.DXGI.Format.B8G8R8A8_UNorm,
                    OptionFlags = ResourceOptionFlags.None,
                    Usage = ResourceUsage.Dynamic,
                    FilterFlags = FilterFlags.Linear,
                    MipFilterFlags = FilterFlags.Linear,
                    FirstMipLevel = 0,
                    MipLevels = 1,
                };

                Texture3DDescription desc = new Texture3DDescription();
                desc.MipLevels = 1;
                desc.BindFlags = BindFlags.ShaderResource;
                desc.CpuAccessFlags = CpuAccessFlags.Write;
                desc.Format = SlimDX.DXGI.Format.B8G8R8A8_UNorm;
                desc.Usage = ResourceUsage.Dynamic;
                desc.Height = width;
                desc.Width = height;
                desc.Depth = depth;
                desc.OptionFlags = ResourceOptionFlags.None;
                texture = new Texture3D(DeviceManager.Instance.device, desc);

                DataBox db = DeviceManager.Instance.context.MapSubresource(texture, 0, MapMode.WriteDiscard, SlimDX.Direct3D11.MapFlags.None);
                //Marshal.Copy(data, 0, db.Data, data.Length);

                db.Data.Write(data, 0, data.Length);
                DeviceManager.Instance.context.UnmapSubresource(texture, 0);

                //int stride = Marshal.SizeOf(typeof(SlimDX.DXGI.Format.B8G8R8A8_UNorm)); 

                //texture = Texture3D.FromMemory(DeviceManager.Instance.device, data, loadInfo);
                m_textureView = new ShaderResourceView(DeviceManager.Instance.device, texture);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        //public void SetTexture(Bitmap bmp)
        //{

        //System.Drawing.Imaging.BitmapData bitmapData = bmp.LockBits(
        //  new Rectangle(0, 0, bmp.Width, bmp.Height),
        //  System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);

        //DataStream dataStream = new DataStream(bitmapData.Scan0, bitmapData.Stride * bitmapData.Height, true, false);
        //DataRectangle dataRectangle = new DataRectangle(bitmapData.Stride, dataStream);

        //try
        //{
        //    //Load the texture
        //    texture = new Texture2D(DeviceManager.Instance.device, new Texture2DDescription()
        //    {
        //        BindFlags = BindFlags.ShaderResource,
        //        CpuAccessFlags = CpuAccessFlags.None,
        //        Format = Format.B8G8R8A8_UNorm,
        //        OptionFlags = ResourceOptionFlags.None,
        //        MipLevels = 1,
        //        Usage = ResourceUsage.Immutable,
        //        Width = bmp.Width,
        //        Height = bmp.Height,
        //        ArraySize = 1,
        //        SampleDescription = new SampleDescription(1, 0)
        //    }, dataRectangle);
        //}
        //finally
        //{
        //    //Free bitmap-access resources
        //    dataStream.Dispose();
        //    bmp.UnlockBits(bitmapData);
        //}


        //m_textureView = new ShaderResourceView(DeviceManager.Instance.device, texture);
        //}

        public void SetColor(System.Drawing.Color color)
        {
            float r = (float)(color.R / 255.0f);
            float g = (float)((float)color.G / 255.0f);
            float b = (float)(color.B / 255.0f);


            Color4 col = new Color4(1, (float)(color.R / 255.0f), (float)((float)color.G / 255.0f), (float)(color.B / 255.0f));
            diffuse = col;
        }

        EffectWrapperTexture3DEffect ew = ShaderManager.Instance.texture3DEffect;

        public override void Render()
        {
            Matrix ViewPerspective = CameraManager.Instance.ViewPerspective;
            Matrix WorldViewPerspective = this.transform * ViewPerspective;
            Matrix WorldView = this.transform * CameraManager.Instance.ViewPerspective;

            ew.WVmat.SetMatrix(WorldView);

            WorldView.M14 = 0;
            WorldView.M24 = 0;
            WorldView.M34 = 0;
            WorldView.M44 = 1;

            WorldView.M41 = 0;
            WorldView.M42 = 0;
            WorldView.M43 = 0;

            ew.WVInvmat.SetMatrix(Matrix.Invert(WorldView));
            ew.tmat.SetMatrix(WorldViewPerspective);

            // configure the Input Assembler portion of the pipeline with the vertex data
            DeviceManager.Instance.context.InputAssembler.InputLayout = ew.layout;
            DeviceManager.Instance.context.InputAssembler.PrimitiveTopology = PrimitiveTopology.TriangleList;

            DeviceManager.Instance.context.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, vertexStride, 0));
            DeviceManager.Instance.context.InputAssembler.SetIndexBuffer(indexBuffer, Format.R16_UInt, 0);
            ew.textureResourceVariable.SetResource(m_textureView);

            ew.technique = ew.effect.GetTechniqueByName("Render");

            EffectTechniqueDescription techDesc;
            techDesc = ew.technique.Description;

            for (int p = 0; p < techDesc.PassCount; ++p)
            {
                ew.technique.GetPassByIndex(p).Apply(DeviceManager.Instance.context);
                DeviceManager.Instance.context.DrawIndexed(numIndices, 0, 0);
            }
        }

        public override void Dispose()
        {

        }

        public override SlimDX.Matrix Transform
        {
            get
            {
                return transform;
            }
            set
            {
                transform = value;
            }
        }
    }
}
